package com.crane.oss.common.service.impl;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import com.crane.common.core.exception.BizException;
import com.crane.oss.properties.AliyunOSSProperties;
import com.crane.oss.common.service.AliyunOSSService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@Service("aliyunOSSService")
public class AliyunOSSServiceImpl implements AliyunOSSService {

    private static final Logger logger = LoggerFactory.getLogger(AliyunOSSServiceImpl.class);

    @Autowired
    protected AliyunOSSProperties aliyunOSSProperties;

    @Override
    public String uploadImage(byte[] localData, String remoteFileName) {
    	logger.info("--->uploadImage 1, remoteFileName={}", remoteFileName);
        long s1 = System.currentTimeMillis();
        try {
            String fileName = "image/" + remoteFileName;
            // 创建OSSClient实例
            OSSClient ossClient = new OSSClient(aliyunOSSProperties.getEndpoint(), aliyunOSSProperties.getAccessKeyId(), aliyunOSSProperties.getAccessKeySecret());
            // 上传Byte数组
            ossClient.putObject(aliyunOSSProperties.getBucketName(), fileName, new ByteArrayInputStream(localData));
            // 关闭OSSClient
            ossClient.shutdown();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException();
        }
        long s2 = System.currentTimeMillis();
        logger.info("阿里云上传图片Service用时：{}", s2 - s1);
        return aliyunOSSProperties.getImagePath() + remoteFileName;
    }

    @Override
    public void deleteFile(String remoteFileName, String filePath) {
    	logger.info("--->deleteFile, remoteFileName={}, filePath={}", remoteFileName, filePath);
        try {
            String fileName = filePath + remoteFileName;
            // 创建OSSClient实例
            OSSClient ossClient = new OSSClient(aliyunOSSProperties.getEndpoint(), aliyunOSSProperties.getAccessKeyId(), aliyunOSSProperties.getAccessKeySecret());
            // 删除文件
            ossClient.deleteObject(aliyunOSSProperties.getBucketName(), fileName);
            // 关闭OSSClient
            ossClient.shutdown();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException();
        }

    }

    /**
     * 分片上传
     *
     * @param ossClient
     * @param file
     * @param fileName
     * @throws AppBizException
     */
    private void partUpload(OSSClient ossClient, File file, String fileName) {
        try {
            /**
             * 步骤1：初始化一个分片上传事件
             */
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(aliyunOSSProperties.getBucketName(), fileName);
            InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);
            // 返回uploadId，它是分片上传事件的唯一标识，您可以根据这个ID来发起相关的操作，如取消分片上传、查询分片上传等
            String uploadId = result.getUploadId();

            /**
             * 步骤2：上传分片 partETags是PartETag的集合。PartETag由分片的ETag和分片号组成
             */
            List<PartETag> partETags = new ArrayList<PartETag>();
            // 计算文件有多少个分片。
            final long partSize = 1 * 1024 * 1024L; // 1MB
            long fileLength = file.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }

            // 遍历分片上传
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                InputStream instream = new FileInputStream(file);
                // 跳过已经上传的分片。
                instream.skip(startPos);
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(aliyunOSSProperties.getBucketName());
                uploadPartRequest.setKey(fileName);
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(instream);
                // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
                uploadPartRequest.setPartNumber(i + 1);
                // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上传分片之后，OSS的返回结果会包含一个PartETag。PartETag将被保存到partETags中。
                partETags.add(uploadPartResult.getPartETag());
            }

            /**
             * 步骤3：完成分片上传。 排序。partETags必须按分片号升序排列。
             */
            Collections.sort(partETags, new Comparator<PartETag>() {
                public int compare(PartETag p1, PartETag p2) {
                    return p1.getPartNumber() - p2.getPartNumber();
                }
            });

            // 在执行该操作时，需要提供所有有效的partETags。OSS收到提交的partETags后，会逐一验证每个分片的有效性。当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
            CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
                    aliyunOSSProperties.getBucketName(), fileName, uploadId, partETags);
            ossClient.completeMultipartUpload(completeMultipartUploadRequest);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException();
        }
    }

	@Override
	public String uploadFile(File file, String remoteFileName) {
		logger.info("--->uploadFile, remoteFileName={}", remoteFileName);
        long s1 = System.currentTimeMillis();
        try {
            String fileName = "file/" + remoteFileName;
            // 创建OSSClient实例
            OSSClient ossClient = new OSSClient(aliyunOSSProperties.getEndpoint(), aliyunOSSProperties.getAccessKeyId(), aliyunOSSProperties.getAccessKeySecret());

            this.partUpload(ossClient, file, fileName);

            // 关闭OSSClient。
            ossClient.shutdown();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException();
        }

        long s2 = System.currentTimeMillis();
        logger.info("阿里云上传文件Service用时：{}", s2 - s1);
        return aliyunOSSProperties.getFilePath() + remoteFileName;
	}

    @Override
    public String uploadVideo(File file, String remoteFileName) {
    	logger.info("--->uploadVideo, remoteFileName={}", remoteFileName);
        long s1 = System.currentTimeMillis();
        try {
            String fileName = "video/" + remoteFileName;
            // 创建OSSClient实例
            OSSClient ossClient = new OSSClient(aliyunOSSProperties.getEndpoint(), aliyunOSSProperties.getAccessKeyId(), aliyunOSSProperties.getAccessKeySecret());

            this.partUpload(ossClient, file, fileName);

            // 关闭OSSClient。
            ossClient.shutdown();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException();
        }

        long s2 = System.currentTimeMillis();
        logger.info("阿里云上传视频Service用时：{}", s2 - s1);
        return aliyunOSSProperties.getVideoPath() + remoteFileName;
    }

    @Override
    public String uploadImage(File file, String remoteFileName) {
    	logger.info("--->uploadImage 2, remoteFileName={}", remoteFileName);
        long s1 = System.currentTimeMillis();
        try {
            String fileName = "image/" + remoteFileName;
            // 创建OSSClient实例
            OSSClient ossClient = new OSSClient(aliyunOSSProperties.getEndpoint(), aliyunOSSProperties.getAccessKeyId(), aliyunOSSProperties.getAccessKeySecret());

            this.partUpload(ossClient, file, fileName);

            // 关闭OSSClient
            ossClient.shutdown();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException();
        }

        long s2 = System.currentTimeMillis();
        logger.info("阿里云上传图片Service用时：{}", s2 - s1);
        return aliyunOSSProperties.getImagePath() + remoteFileName;
    }

}
